#include "laurin-discover-devices-module.h"
#include "laurin-device.h"




void processFrameExtForDevices(u_char* arg, const struct pcap_pkthdr *header,const u_char *data)
{
	((LaurinDiscoverDevicesModule*)arg)->processFrame(header, data);
}


LaurinDiscoverDevicesModule::LaurinDiscoverDevicesModule(const Glib::ustring& ifaceName, const Glib::ustring& masterIfaceName): LaurinModule(Glib::ustring("LAURIN DISCOVER DEVICES"))
{
	_max = 1000;
	LaurinDevice* master = new LaurinDevice(masterIfaceName);
	_device = new LaurinMonitorDevice(ifaceName, master);

	delete master;
}


LaurinDiscoverDevicesModule::~LaurinDiscoverDevicesModule()
{
	clearResources();
	delete _device;
}


bool LaurinDiscoverDevicesModule::isInitialized()
{
	return _device->isInitialized();
}


void LaurinDiscoverDevicesModule::run()
{
	clearResources();

	pcap_t* context = _device->getCaptureContext();
	bool ctrl = true;
	int counter = -1;

	while(ctrl && counter < _max)
	{
		int r = pcap_dispatch(context, 1, processFrameExtForDevices, (u_char*) this);
		counter++;

		// r == -2 came from pcap_breakloop()
		ctrl = !isStopRequestPending() && r != -2;
	}
}


void LaurinDiscoverDevicesModule::innerStop()
{
	pcap_breakloop(_device->getCaptureContext());
}


LaurinNetworkSet* LaurinDiscoverDevicesModule::getNetworkSet()
{
	if(isWorking())
		return NULL;

	return _networks;
}


void LaurinDiscoverDevicesModule::processFrame(const struct pcap_pkthdr *header,const u_char *data)
{

	// parse the frame, only essential data
	Laurin80211Frame frame;
	if(!frame.setFrame(data, header->caplen) || !frame.parse80211FrameType())
		return;


	bool accept = false;
	bool isap = false;


	switch(frame.getFrameType())
	{
		case IEEE80211_FTYPE_DATA:
			if(frame.getFrameSubType() == IEEE80211_STYPE_DATA)
				accept = true;

			break;

			/*
		case IEEE80211_FTYPE_CTL:
			if( (subtype & IEEE80211_STYPE_CTS) ||
				(subtype & IEEE80211_STYPE_RTS) ||
				(subtype & IEEE80211_STYPE_ACK) )
				accept = true;
			*/

		case IEEE80211_FTYPE_MGMT:
			if(frame.getFrameSubType() & IEEE80211_STYPE_BEACON )
			{
				accept = true;
				isap = true;
			}
			break;
	}



	if(accept)
	{

		//only now we parse the frame informations

		if(!frame.parse80211Header())
			return;


		// this tries to avoid that a lot of packets will be considered
		// only in the second part of the elaborate method.
		if(isap)
		{

			/*  the beacon frame are the most common ones,
				so we can have a very large number of they, and this is useless.
				hence we consider a beacon from an ap only once ( a bssid test)
				the test is fast because it's unlikely that there are many
				ap nearby, so the length of the list isn't so large. */

			std::list<u_int8_t*>::iterator b = _ap.begin();
			std::list<u_int8_t*>::iterator e = _ap.end();

			bool stop = false;
			while(b != e && !stop)
			{
				stop = areMACEquals(*b, frame.getBSSID());
				b++;
			}


			if(!stop)
			{
				if(!frame.parseRadiotapHeader())
					return;

				u_int8_t* tmp = (u_int8_t*) malloc(ETH_ALEN);
				memcpy(tmp, frame.getBSSID(), ETH_ALEN);

				_ap.push_front(tmp);

				_frames.push_front(frame);
			}

		}
		else
		{
			if(!frame.parseRadiotapHeader())
				return;

			_frames.push_back(frame);
		}
	}

}


void LaurinDiscoverDevicesModule::elaborateFrames()
{
	if(isWorking())
		return;

	std::list<Laurin80211Frame>::iterator b = _frames.begin();
	std::list<Laurin80211Frame>::iterator e = _frames.end();

	// used for save the frames that aren't processed on first pass.
	// we use iterator to the _frames list instead of coping the entire frame
	std::list<std::list<Laurin80211Frame>::iterator> _clients;


	Laurin80211Network* n = NULL;
	Glib::ustring ssid;

	_networks = new LaurinNetworkSet();


	while(b != e)
	{
		switch((*b).getFrameType())
		{

			// we have only BEACON frame, so isn't necessary check the subtype value
			case IEEE80211_FTYPE_MGMT:

				// not consider ibss frame
				if((*b).isIBBSFrame())
					break;

				// we get the ssid and we search the network where the ssid belongs.
				// if no network is present, then a new one will be created.
				// after that the access point will be added only if its bssid is not alredy present.

				ssid = (*b).getSSID();
				n = _networks->searchNetworkFromSSID(ssid);


				// network not found
				if(!n)
					n = _networks->addNetwork(ssid, false);



				// is this access point present on the network?
				if(!(n->isAccessPointPresent((*b).getBSSID())))
				{
					LaurinAccessPoint ap = LaurinAccessPoint((*b).getBSSID());

					ap.setSSID(ssid);
					ap.setInterval((*b).getInterval());
					ap.setNetworkEncrypted((*b).isNetworkEncrypted());
					ap.setPCFAvaible((*b).isPCFAvaible);
					ap.setRadioInformations((*b).getRadioInformations());
					n->addAccessPoint(ap, false);
				}

				break;


			case IEEE80211_FTYPE_DATA:

				// we search the network in which the bssid lives, and if exist then we add
				// the station.

				n = _networks->searchNetworkFromBSSID((*b).getBSSID());


				if(n)
				{
					// access point is a station, and we have alredy added it
					if(n->isAccessPointPresent((*b).getSourceAddress()))
						break;

					Laurin80211Station s = Laurin80211Station((*b).getSourceAddress());
					s.setRadioInformations((*b).getRadioInformations());
					n->addStation(s);
				}

				else
					// add for second pass elaboration ( I pass to consider first all the beacon frames)
					_clients.push_front(b);

				break;


			/*case IEEE80211_FTYPE_CTL:
				//TODO
				break;
			*/
		}


		b++;

	}



	// second pass, elaborate all frames

	std::list<std::list<Laurin80211Frame>::iterator>::iterator bc = _clients.begin();
	std::list<std::list<Laurin80211Frame>::iterator>::iterator ec = _clients.end();
	std::list<Laurin80211Frame>::iterator aux;

	while(bc != ec)
	{
		aux = *bc;

		Laurin80211Network* n = _networks->searchNetworkFromBSSID((*aux).getBSSID());

		// no beacon from this network?!?
		if(n)
		{
			Laurin80211Station s = Laurin80211Station((*aux).getSourceAddress());
			s.setRadioInformations((*aux).getRadioInformations());
			n->addStation(s);
		}

		bc++;
	}

	_clients.clear();
	_frames.clear();
}


void LaurinDiscoverDevicesModule::clearResources()
{

	//delete frames
	_frames.clear();


	//delete ap
	std::list<u_int8_t*>::iterator b = _ap.begin();
	std::list<u_int8_t*>::iterator e = _ap.end();

	while(b != e)
		free(*b);

	b = _ap.begin();
	_ap.erase(b, e);

	//delete networks
	delete _networks;
}
